module writeback(
        // from previous stage
        input           i_pipe_valid,
        input   [31:2]  i_pipe_pc,
        input   [31:0]  i_pipe_instr,
        input   [14:0]  i_pipe_gpr_we,
        input           i_pipe_gpr_we0,
        input   [ 3:0]  i_pipe_gpr_wa0,
        input           i_pipe_gpr_wa0_exc,
        input   [ 1:0]  i_pipe_gpr_dsel0_wb,
        input   [31:0]  i_pipe_gpr_d0_prev,
        input   [31:0]  i_pipe_gpr_d0_alu,
        input   [31:0]  i_pipe_gpr_d0_mul,
        input   [31:0]  i_pipe_gpr_d0_exta,
        input           i_pipe_gpr_we1,
        input   [ 3:0]  i_pipe_gpr_wa1,
        input           i_pipe_gpr_wa1_usr,
        input           i_pipe_gpr_dsel1,
        input   [31:0]  i_pipe_gpr_d1_ld,
        input   [31:0]  i_pipe_gpr_d1_mul,
        input           i_pipe_cpsr_nzcv_we,
        input   [ 1:0]  i_pipe_cpsr_nzcv_dsel,
        input   [ 3:0]  i_pipe_cpsr_nzcv_d_rm,
        input   [ 3:0]  i_pipe_cpsr_nzcv_d_spsr,
        input   [ 3:0]  i_pipe_cpsr_nzcv_d_alu,
        input   [ 3:0]  i_pipe_cpsr_nzcv_d_mul,
        input   [ 2:0]  i_pipe_cpsr_aif_we,
        input   [ 1:0]  i_pipe_cpsr_aif_dsel,
        input   [ 2:0]  i_pipe_cpsr_aif_d_rm,
        input   [ 2:0]  i_pipe_cpsr_aif_d_spsr,
        input   [ 2:0]  i_pipe_cpsr_aif_d_imm,
        input           i_pipe_cpsr_mode_we,
        input   [ 1:0]  i_pipe_cpsr_mode_dsel,
        input   [ 4:0]  i_pipe_cpsr_mode_d_rm,
        input   [ 4:0]  i_pipe_cpsr_mode_d_spsr,
        input   [ 4:0]  i_pipe_cpsr_mode_d_imm,
        input           i_pipe_spsr_we,
        input           i_pipe_spsr_dsel,
        input   [31:0]  i_pipe_spsr_d_rm,
        input   [31:0]  i_pipe_spsr_d_cpsr,
        input           i_pipe_branch,
        input   [ 1:0]  i_pipe_branch_dest_sel,
        input   [31:2]  i_pipe_branch_dest_ex1,

        // to branch
        output          o_branch_branch,
        output  [31:2]  o_branch_dest,

        // to gpr
        output          o_gpr_we0, // cgen: type=output cname=o_pipe_gpr_we0
        output  [ 3:0]  o_gpr_wa0, // cgen: type=output cname=o_pipe_gpr_wa0
        output          o_gpr_wa0_exc, // cgen: type=output cname=o_pipe_gpr_wa0_exc
        output  [ 4:0]  o_gpr_wa0_exc_mode,
        output  [31:0]  o_gpr_d0, // cgen: type=output cname=o_pipe_gpr_d0
        output          o_gpr_we1, // cgen: type=output cname=o_pipe_gpr_we1
        output  [ 3:0]  o_gpr_wa1, // cgen: type=output cname=o_pipe_gpr_wa1
        output          o_gpr_wa1_usr, // cgen: type=output cname=o_pipe_gpr_wa1_usr
        output  [31:0]  o_gpr_d1, // cgen: type=output cname=o_pipe_gpr_d1

        // to cpsr
        output          o_cpsr_nzcv_we, // cgen: type=output cname=o_pipe_cpsr_nzcv_we
        output  [ 3:0]  o_cpsr_nzcv_d,  // cgen: type=output cname=o_pipe_cpsr_nzcv_d
        output  [ 2:0]  o_cpsr_aif_we,  // cgen: type=output cname=o_pipe_cpsr_aif_we
        output  [ 2:0]  o_cpsr_aif_d,   // cgen: type=output cname=o_pipe_cpsr_aif_d
        output          o_cpsr_mode_we, // cgen: type=output cname=o_pipe_cpsr_mode_we
        output  [ 4:0]  o_cpsr_mode_d,  // cgen: type=output cname=o_pipe_cpsr_mode_d

        // to spsr
        output          o_spsr_we,
        output          o_spsr_wa_exc,
        output  [ 4:0]  o_spsr_wa_exc_mode,
        output  [31:0]  o_spsr_d,

        // pipeline control
        output          o_valid
);

`include "enum.vh"

reg [31:2] branch_dest;
reg [31:0] gpr_d0;
reg [31:0] gpr_d1;
reg [ 3:0] nzcv_d;
reg [ 2:0] aif_d;
reg [ 4:0] mode_d;
reg [31:0] spsr_d;

always @* begin
  casez (i_pipe_branch_dest_sel)
    2'b00 : branch_dest = i_pipe_gpr_d0_alu[31:2];
    2'b01 : branch_dest = i_pipe_gpr_d1_ld[31:2];
    2'b1? : branch_dest = i_pipe_branch_dest_ex1;
  endcase
end

always @* begin
  case (i_pipe_gpr_dsel0_wb)
    GPR_DSEL0_WB_PREV : gpr_d0 = i_pipe_gpr_d0_prev ;
    GPR_DSEL0_WB_ALU  : gpr_d0 = i_pipe_gpr_d0_alu  ;
    GPR_DSEL0_WB_MUL  : gpr_d0 = i_pipe_gpr_d0_mul  ;
    GPR_DSEL0_WB_EXTA : gpr_d0 = i_pipe_gpr_d0_exta ;
    default           : gpr_d0 = 32'bx;
  endcase
end

always @* begin
  case (i_pipe_gpr_dsel1)
    GPR_DSEL1_LD  : gpr_d1 = i_pipe_gpr_d1_ld  ;
    GPR_DSEL1_MUL : gpr_d1 = i_pipe_gpr_d1_mul ;
    default       : gpr_d1 = 32'bx;
  endcase
end

always @* begin
  case (i_pipe_cpsr_nzcv_dsel)
    NZCV_DSEL_RM   : nzcv_d = i_pipe_cpsr_nzcv_d_rm   ;
    NZCV_DSEL_SPSR : nzcv_d = i_pipe_cpsr_nzcv_d_spsr ;
    NZCV_DSEL_ALU  : nzcv_d = i_pipe_cpsr_nzcv_d_alu  ;
    NZCV_DSEL_MUL  : nzcv_d = i_pipe_cpsr_nzcv_d_mul  ;
    default        : nzcv_d = 4'bx;
  endcase
end

always @* begin
  case (i_pipe_cpsr_aif_dsel)
    AIF_DSEL_RM   : aif_d = i_pipe_cpsr_aif_d_rm   ;
    AIF_DSEL_SPSR : aif_d = i_pipe_cpsr_aif_d_spsr ;
    AIF_DSEL_IMM  : aif_d = i_pipe_cpsr_aif_d_imm  ;
    default       : aif_d = 3'bx;
  endcase
end

always @* begin
  case (i_pipe_cpsr_mode_dsel)
    MODE_DSEL_RM   : mode_d = i_pipe_cpsr_mode_d_rm   ;
    MODE_DSEL_SPSR : mode_d = i_pipe_cpsr_mode_d_spsr ;
    MODE_DSEL_IMM  : mode_d = i_pipe_cpsr_mode_d_imm  ;
    default        : mode_d = 5'bx;
  endcase
end

always @* begin
  case (i_pipe_spsr_dsel)
    SPSR_DSEL_RM   : spsr_d = i_pipe_spsr_d_rm   ;
    SPSR_DSEL_CPSR : spsr_d = i_pipe_spsr_d_cpsr ;
    default        : spsr_d = 32'bx;
  endcase
end

assign o_branch_branch = i_pipe_branch;
assign o_branch_dest = branch_dest;

assign o_gpr_we0 = i_pipe_gpr_we0;
assign o_gpr_wa0 = i_pipe_gpr_wa0;
assign o_gpr_wa0_exc = i_pipe_gpr_wa0_exc;
assign o_gpr_wa0_exc_mode = mode_d;
assign o_gpr_d0 = gpr_d0;
assign o_gpr_we1 = i_pipe_gpr_we1;
assign o_gpr_wa1 = i_pipe_gpr_wa1;
assign o_gpr_wa1_usr = i_pipe_gpr_wa1_usr;
assign o_gpr_d1 = gpr_d1;
assign o_cpsr_nzcv_we = i_pipe_cpsr_nzcv_we;
assign o_cpsr_nzcv_d  = nzcv_d;
assign o_cpsr_aif_we = i_pipe_cpsr_aif_we;
assign o_cpsr_aif_d  = aif_d;
assign o_cpsr_mode_we = i_pipe_cpsr_mode_we;
assign o_cpsr_mode_d  = mode_d;
assign o_spsr_we = i_pipe_spsr_we;
assign o_spsr_wa_exc = i_pipe_gpr_wa0_exc;
assign o_spsr_wa_exc_mode = mode_d;
assign o_spsr_d = spsr_d;

assign o_valid = i_pipe_valid;

endmodule
